package pl.edu.pw.MidEmu.Sensing.Processing;

import pl.edu.pw.MidEmu.Sensing.Processing.LoopProcessing.LoopSpecification;
import pl.edu.pw.MidEmu.Sensing.Processing.LoopProcessing.TimeSpecification;
import pl.edu.pw.MidEmu.Sensing.Processing.ValueProcessing.ValueSpecification;
import pl.edu.pw.MidEmu.Sensing.Sensors.ASensor;

/**
 * Class responsible for generating sensor values using loop specifications and time specfications 
 * @author Mateusz Starodab
 * @see LoopProcessing
 */
public class ProcessingAlgorithm extends Thread
{
	private int iterationsCount;

	private LoopProcessing loopProcessing;

	private boolean pause = false;

	private ASensor sensor;
	private boolean terminate = false;

	private ValueProcessing valueProcessing;

	private int valuesRead;

	/**
	 * Constructor of p[rocessing algorithm
	 * @param loopProcessing Loops processing to be used
	 * @param valueProcessing Value processing to be used
	 */
	public ProcessingAlgorithm(LoopProcessing loopProcessing, ValueProcessing valueProcessing)
	{
		this.loopProcessing = loopProcessing;
		this.valueProcessing = valueProcessing;
		iterationsCount = 0;
		valuesRead = 0;
	}

	/**
	 * Checks if algorithm is running
	 * @return True if algorithm is running
	 */
	public boolean isRunning()
	{
		return !pause;
	}

	/**
	 * Pauses algorithm processing
	 */
	public void pause()
	{
		pause = true;
	}

	/**
	 * Starts processing algorithm in separate Thread\
	 * Main loop of the Thread
	 */
	@Override
	public void run()
	{
		if (loopProcessing.getLoopSpecification() == LoopSpecification.Endless)
		{
			while (true)
			{
				if (valueProcessing.getValueSpecification() == ValueSpecification.Increment)
				{
					sensor.getValue().incrementValue();
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.RangeIncrement)
				{
					if (sensor.getValue().compare(valueProcessing.getHighLimit()) < 0)
						sensor.getValue().incrementValue();
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.Decrement)
				{
					sensor.getValue().decrementValue();
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.RangeDecrement)
				{
					if (sensor.getValue().compare(valueProcessing.getLowLimit()) > 0)
						sensor.getValue().decrementValue();
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.RangeRandom)
				{
					sensor.getValue().randomValue(valueProcessing.getLowLimit(), valueProcessing.getHighLimit());
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.Random)
				{
					sensor.getValue().randomValue();
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.External)
				{
					if (valuesRead == valueProcessing.getExternalValues().size())
					{
						iterationsCount = 0;
						valuesRead = 0;
					}
					if (valueProcessing.getExternalValues().get(iterationsCount) != null)
					{
						sensor.parseValue(valueProcessing.getExternalValues().get(iterationsCount));
						valuesRead++;
					}
				}
				if (loopProcessing.getTimeSpecification() == TimeSpecification.Interval)
				{
					try
					{
						sleep(loopProcessing.getIntervalTime());
					}
					catch (InterruptedException ex)
					{
						ex.printStackTrace();
					}
				}
				while (pause)
				{
					try
					{
						sleep(250);
					}
					catch (InterruptedException ex)
					{
						ex.printStackTrace();
					}
				}
				if (terminate)
					break;
				iterationsCount++;
			}
		}
		else if (loopProcessing.getLoopSpecification() == LoopSpecification.Incremental)
		{
			for (int i = 0; i < loopProcessing.getIterationCount(); i++)
			{
				if (valueProcessing.getValueSpecification() == ValueSpecification.Increment)
				{
					sensor.getValue().incrementValue();
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.RangeIncrement)
				{
					if (sensor.getValue().compare(valueProcessing.getHighLimit()) > 0)
						sensor.getValue().incrementValue();
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.Decrement)
				{
					sensor.getValue().decrementValue();
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.RangeDecrement)
				{
					if (sensor.getValue().compare(valueProcessing.getLowLimit()) > 0)
						sensor.getValue().decrementValue();
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.RangeRandom)
				{
					sensor.getValue().randomValue(valueProcessing.getLowLimit(), valueProcessing.getHighLimit());
				}
				else if (valueProcessing.getValueSpecification() == ValueSpecification.Random)
				{
					sensor.getValue().randomValue();
				}
				if (loopProcessing.getTimeSpecification() == TimeSpecification.Interval)
				{
					try
					{
						sleep(loopProcessing.getIntervalTime());
					}
					catch (InterruptedException ex)
					{
						ex.printStackTrace();
					}
				}
				while (pause)
				{
					try
					{
						sleep(250);
					}
					catch (InterruptedException ex)
					{
						ex.printStackTrace();
					}
				}
				if (terminate)
					break;
			}
		}
	}


	/**
	 * Sets sensor for this processing algorithm
	 * @param sensor Sensor to be assignet to this algorithm instance
	 */
	public void setSensor(ASensor sensor)
	{
		this.sensor = sensor;
	}

	/**
	 * Terminate processing algorithm
	 * Calling this method will result in sensor termiantion
	 */
	public void terminate()
	{
		pause = false;
		terminate = true;
	}

	/**
	 * Resumes processing
	 */
	public void unpause()
	{
		pause = false;
	}
}
